package cate.game.pvp.pointrace.simulator;

import cate.game.framework.advanced.AdvancedLongAttributes;
import cate.game.pvp.pointrace.data.PointRaceKV;
import cate.game.pvp.pointrace.handler.PointRaceChallenge;
import cate.game.pvp.pointrace.handler.PointRacePsOperation;
import cate.game.pvp.pointrace.timer.PointRaceTimerTask.Settings;

import java.util.Objects;
import java.util.function.Supplier;

import static cate.game.event.pointrace.PointRaceTimerEvent.EventType.*;

public class BothPointRaceControlSimulator extends PointRaceSimulator {

    private final Supplier<PointRacePsOperation> psOperationSupplier;
    private final Supplier<PointRaceChallenge> challengeSupplier;

    public BothPointRaceControlSimulator(Supplier<PointRaceKV> kvSupplier,
                                         Supplier<PointRacePsOperation> psOperationSupplier,
                                         Supplier<PointRaceChallenge> challengeSupplier) {
        super(kvSupplier);
        this.psOperationSupplier = Objects.requireNonNull(psOperationSupplier);
        this.challengeSupplier = Objects.requireNonNull(challengeSupplier);
    }

    @Override
    public boolean isMatch(Settings settings) {
        return settings.getEventType() == OPEN
                || settings.getEventType() == CLOSE
                || settings.getEventType() == SWITCH
                || settings.getEventType() == DAY_SPAN;
    }

    @Override
    public void runWith(Settings settings) {
        switch (settings.getEventType()) {
            case OPEN:
                runOpenWith(settings);
                break;
            case SWITCH:
                runSwitchWith(settings);
                break;
            case CLOSE:
                runCloseWith(settings);
                break;
            case DAY_SPAN:
                runDaySpanWith(settings);
                break;
            default:
                break;
        }
    }

    private void runDaySpanWith(Settings settings) {
        AdvancedLongAttributes attributes = getAttributes();
        PointRaceKV kv = getKV();
        if (attributes.hasAttribute(DAY_SPAN.getFlag() + settings.getDay())) {
            return;
        }

        kv.setDay(settings.getDay());

        attributes.setAttribute(DAY_SPAN.getFlag() + settings.getDay());
    }

    private void runCloseWith(Settings settings) {
        AdvancedLongAttributes attributes = getAttributes();
        if (attributes.hasAttribute(CLOSE.getFlag())) {
            return;
        }

        PointRacePsOperation psOperation = getPsOperation();
        synchronized (challengeSupplier.get()) {
            psOperation.updatePoints();
        }

        attributes.setAttribute(CLOSE.getFlag());
    }

    private void runSwitchWith(Settings settings) {
        AdvancedLongAttributes attributes = getAttributes();
        if (attributes.hasAttribute(SWITCH.getFlag())) {
            // no-op
            return;
        }

        PointRacePsOperation psOperation = getPsOperation();
        synchronized (challengeSupplier.get()) {
            psOperation.updatePoints();
        }

        PointRaceKV kv = getKV();
        kv.getTimerSettings().forEach(s -> {
            s.setSeason(settings.getSeason() + 1);
            s.setTriggerTimeMs(s.getTriggerTimeMs() + kv.getCycleTimeMs());
        });

        attributes.setAttribute(SWITCH.getFlag());
    }

    private void runOpenWith(Settings settings) {
        AdvancedLongAttributes attributes = getAttributes();
        PointRaceKV kv = getKV();
        if (attributes.hasAttribute(OPEN.getFlag())
                && settings.getSeason() == kv.getSeason()) {
            return;
        }

        attributes.clear();
        kv.getEventAttributes().clear();
        kv.setSeason(settings.getSeason());
        kv.setDay(0);
        kv.setOpenTime(settings.getTriggerTimeMs());
        kv.setClosedTime(settings.getTriggerTimeMs() + kv.getCycleTimeMs());

        attributes.setAttribute(OPEN.getFlag());
    }

    private PointRacePsOperation getPsOperation() {
        return Objects.requireNonNull(psOperationSupplier.get());
    }
}
